{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Addition"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this model, you will see a transformation which is the basic property of single neurons (i.e., addition). Addition transforms two inputs into a single output which is their sum. You will construct a network that adds two inputs. The network utilizes two communication channels going into the same neural population. Addition is somewhat ‘free’, since the incoming currents from different synaptic connections interact linearly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Setup the environment\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "import nengo\n",
    "from nengo.dists import Uniform\n",
    "from nengo.utils.functions import piecewise"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create the Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This model has ensembles A and B which represent the two inputs to be added. The 'Sum' ensemble represents the added value. All the parameters used in the model are as described in the book, with the sum ensemble having a radius of 2 to account for the maximum range of summing the input values. \n",
    "\n",
    "While connecting the inputs to the ensembles A and B, the transform is set to 1 (which is the default value) since this should be a communication channel. However as described in the book, you can scale a represented variable by a constant value by changing the transform. Example: if you set the transform of ensemble B to 0 and ensemble A to 2 (i.e., nengo.Connection(input_A, A, transform=[2]) ), the sum will be twice of the input_A. You will also need to set an appropriate radius for the Sum ensemble to avoid saturation when you change the transform values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Create the network\n",
    "model = nengo.Network(label='Scalar Addition')\n",
    "\n",
    "with model:\n",
    "    # Inputs to drive the activity in ensembles A and B \n",
    "    input_A = nengo.Node(piecewise({0: -0.75, 1.25: 0.5, 2.5: 0.70, 3.75: 0}))\n",
    "    input_B = nengo.Node(piecewise({0: 0.25, 1.25: -0.5, 2.5: 0.85, 3.75: 0}))\n",
    "\n",
    "    # Ensembles with 100 LIF neurons each\n",
    "    A = nengo.Ensemble(100, dimensions=1, max_rates=Uniform(100, 200))             # Represents the first input\n",
    "    B = nengo.Ensemble(100, dimensions=1, max_rates=Uniform(100, 200))             # Represents the second input\n",
    "    Sum = nengo.Ensemble(100, dimensions=1, max_rates=Uniform(100, 200), radius=2) # Reprsents the sum of two inputs\n",
    "        \n",
    "    # Connecting the input nodes to ensembles\n",
    "    nengo.Connection(input_A, A)\n",
    "    nengo.Connection(input_B, B)\n",
    "    \n",
    "    # Connecting ensembles A and B to the Sum ensemble\n",
    "    nengo.Connection(A, Sum)\n",
    "    nengo.Connection(B, Sum)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Probes to Collect Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "with model:\n",
    "    input_A_probe = nengo.Probe(input_A)        # Input to ensemble A\n",
    "    input_B_probe = nengo.Probe(input_B)        # Input to ensemble B\n",
    "    A_probe = nengo.Probe(A, synapse=0.01)      # Decoded ouput of ensemble A\n",
    "    B_probe = nengo.Probe(B, synapse=0.01)      # Decoded ouput of ensemble B\n",
    "    Sum_probe = nengo.Probe(Sum, synapse=0.01)  # Decoded ouput of Sum ensemble"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Import the nengo_gui visualizer to run and visualize the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from nengo_gui.ipython import IPythonViz\n",
    "IPythonViz(model, \"ch3-addition.py.cfg\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Press the play button in the visualizer to run the simulation. You should see the graphs as shown in the figure below.\n",
    "\n",
    "The input_A and input_B graphs show the inputs to ensembles A and B respectively. The graphs A and B show the decoded value of the activity of ensembles A and B respectively. The sum graph shows that the decoded value of the activity in the Sum ensemble provides a good estimate of the sum of inputs A and B.\n",
    "\n",
    "You can use the sliders to change the input values provided by the input_A and input_B nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image\n",
    "Image(filename='ch3-addition.png')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  },
  "widgets": {
   "state": {},
   "version": "1.1.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
